# HTTP 请求封装（requests 二次封装，统一请求方法）
import json
import configparser
from pathlib import Path
from typing import Dict, List, Optional, Union
import requests
from dataclasses import dataclass
from functools import wraps
from db_operation import MySQLClient


@dataclass
class RequestResult:
    success: bool
    status_code: Optional[int] = None
    response: Optional[Union[Dict, List, str]] = None
    error: Optional[str] = None
    case_id: Optional[int] = None
    url: Optional[str] = None


def handle_errors(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except requests.RequestException as e:
            return RequestResult(False, error=f"Request failed: {e}")
        except json.JSONDecodeError as e:
            return RequestResult(False, error=f"JSON error: {e}")
        except Exception as e:
            return RequestResult(False, error=f"Error: {e}")

    return wrapper


class RequestHandler:
    def __init__(self, config_path=None):
        self._config = self._load_config(config_path)
        self.base_url = self._config.get('api', 'base_url', fallback='').rstrip('/')
        self._session = self._create_session()
        self._test_results = {}

    def _load_config(self, config_path):
        config = configparser.ConfigParser()
        paths = [config_path] if config_path else []
        script_dir = Path(__file__).parent
        paths.extend([
            'config.ini',
            str(script_dir / 'config.ini'),
            str(script_dir.parent / 'config' / 'config.ini')
        ])
        if not config.read(paths, encoding='utf-8'):
            raise FileNotFoundError(f"Config not found in: {paths}")
        return config

    def _create_session(self):
        session = requests.Session()
        session.verify = self._config.getboolean('api', 'verify_ssl', fallback=True)
        session.timeout = self._config.getfloat('api', 'timeout', fallback=10)

        headers = self._config.get('api', 'headers', fallback='{}')
        try:
            session.headers.update(json.loads(headers))
        except json.JSONDecodeError:
            session.headers.update({'Content-Type': 'application/json'})
        return session

    @handle_errors
    def process_requests(self, case_ids=None, filters=None):
        db = MySQLClient()
        try:
            for row in db.select('gdyh'):
                if not (case := self._parse_case(row)):
                    continue
                if not self._should_process(case, case_ids, filters):
                    continue

                data = self._process_deps(case['data'])
                result = self._send_request(case['method'], case['url'], data, case['id'])
                self._test_results[case['id']] = result
                self._validate(result, case)
        finally:
            db.close()
        return self._test_results

    def _parse_case(self, row):
        try:
            return {
                'id': row['id'],
                'method': str(row['methon']).lower(),
                'url': f"{self.base_url}/{str(row['url']).lstrip('/')}",
                'data': row['input'],
                'expected': row['output'],
                'dependencies': row.get('dependencies')
            }
        except (IndexError, TypeError) as e:
            print(f"Parse error: {e}")
            return None

    def _should_process(self, case, case_ids, filters):
        # case_ids 为 None 或空列表时执行所有用例
        if case_ids is not None and len(case_ids) > 0 and case['id'] not in case_ids:
            return False
        if not filters:
            return True
        return all(case.get(k) == v for (k, v) in filters.items())

    def _process_deps(self, data):
        if not data or not isinstance(data, str):
            return data

        for case_id in self._test_results:
            res = self._test_results[case_id]
            if res.success and res.response:
                ph = f"${{{case_id}}}"
                if ph in data:
                    rep = json.dumps(res.response) if isinstance(res.response, (dict, list)) else str(res.response)
                    data = data.replace(ph, rep)

        try:
            return json.loads(data) if data.startswith(('{', '[')) else data
        except Exception as e:
            print(f"Dependency error: {e}")
            return data

    @handle_errors
    def _send_request(self, method, url, data, case_id=None):
        method = method.lower()
        if method not in ('get', 'post', 'put', 'delete', 'patch'):
            raise ValueError(f"Invalid method: {method}")

        prepped = self._prepare_data(data)
        kwargs = {'json': prepped} if isinstance(prepped, (dict, list)) else {'data': prepped}

        resp = getattr(self._session, method)(url, **kwargs)
        resp.raise_for_status()

        try:
            resp_data = resp.json() if resp.content else None
        except json.JSONDecodeError:
            resp_data = resp.text

        return RequestResult(True, resp.status_code, resp_data, case_id=case_id, url=url)

    def _prepare_data(self, data):
        if isinstance(data, str):
            try:
                return json.loads(data)
            except json.JSONDecodeError:
                return data
        return data

    def _validate(self, result, case):
        if not result.success:
            print(f"Case {case['id']} failed: {result.error}")
            return False

        expected = self._parse_expected(case['expected'])
        if expected is not None and result.response != expected:
            print(f"\nValidation failed - Case {case['id']}")
            print(f"URL: {case['url']}")
            print(f"Expected: {expected}\nActual: {result.response}")
            print("-" * 50)
            return False
        return True

    def _parse_expected(self, expected):
        if isinstance(expected, str):
            try:
                return json.loads(expected)
            except json.JSONDecodeError:
                return expected
        return expected


if __name__ == '__main__':
    try:
        handler = RequestHandler()
        results = handler.process_requests(case_ids=[])

        print("\nResults:", results)


    except Exception as e:
        print(f"Error: {e}")